package com.chenfan.mcn.extension.excel;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelEntity;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.export.ExcelExportService;
import cn.afterturn.easypoi.exception.excel.ExcelExportException;
import cn.afterturn.easypoi.exception.excel.enums.ExcelExportEnum;
import cn.afterturn.easypoi.handler.inter.IReadHandler;
import cn.afterturn.easypoi.util.PoiPublicUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.chenfan.common.exception.BusinessException;
import com.chenfan.mcn.exception.McnErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.Function;

/**
 * 参考 cn.afterturn.easypoi.excel.ExcelExportUtil
 * @Author liuyang
 * @Date 2021/1/8
 * @Time 16:01
 * @Version V1.0
 **/
@Slf4j
public final class ExcelUtils {

    public static int USE_SXSSF_LIMIT = 100000;

    public static void exportExcel(
            List<?> list,
            ExportParams exportParams,
            Class<?> pojoClass,
            String fileName,
            HttpServletResponse response) throws Exception {
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    public static void exportExcel(
            List<?> list,
            String sheetName,
            Class<?> pojoClass,
            String fileName,
            HttpServletResponse response)
            {
        exportExcel(list, null, sheetName, pojoClass, fileName, response);
    }

    public static void exportExcel(
            List<?> list,
            String title,
            String sheetName,
            Class<?> pojoClass,
            String fileName,
            HttpServletResponse response)
             {
        defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName));
    }

    private static void defaultExport(
            List<?> list,
            Class<?> pojoClass,
            String fileName,
            HttpServletResponse response,
            ExportParams exportParams)
             {
        exportParams.setStyle(CustomExcelExportStyler.class);
                 exportParams.setDataHandler(new EnhancedExcelDataHandler());
        Workbook workbook = getWorkbook(exportParams.getType(), list.size());
        new CustomExcelExportService().createSheet(workbook, exportParams, pojoClass, list);
        if (workbook != null) {
            downLoadExcel(fileName, response, workbook);
        }
    }

    public static void exportDynamicExcel(HttpServletResponse response, String fileName, List<DynamicExcel> dynamicExcelList)  {
        Workbook workbook = getWorkbook(ExcelType.HSSF, 0);
        for(DynamicExcel dynamicExcel : dynamicExcelList) {
            ExportParams exportParams = new ExportParams();
            exportParams.setSheetName(dynamicExcel.getSheetName());
            exportParams.setStyle(CustomExcelExportStyler.class);
            if(Objects.nonNull(dynamicExcel.getEntityClass())) {
                new CustomExcelExportService().createSheet(workbook, exportParams, dynamicExcel.getEntityClass(), dynamicExcel.getDataSet());
            } else {
                new CustomExcelExportService().createSheetForMap(workbook, exportParams, dynamicExcel.getEntityList(), dynamicExcel.getDataSet());
            }
        }
        try {
            downLoadExcel(fileName, response, workbook);
        } catch (Exception e) {
            throw new BusinessException(McnErrorCode.EXPORT_EXCEPTION);
        }
    }

    private static void downLoadExcel(
            String fileName, HttpServletResponse response, Workbook workbook)  {
        ServletOutputStream outputStream = null;
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader(
                    "Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
            outputStream.flush();
        } catch (IOException e) {
            log.error("下载异常", e);
            throw new BusinessException(McnErrorCode.EXPORT_EXCEPTION);
        } finally {
            try {
                assert outputStream != null;
                outputStream.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 自定义导出
     */
    public static void customExport(String fileName, String title, String sheetName, List<ExcelCustom> paramInfo, Class<?> pojoClass, List<?> dataSet, HttpServletResponse response) {
        ExportParams exportParams = new ExportParams(title, sheetName);
        exportParams.setStyle(CustomExcelExportStyler.class);
        List<ExcelExportEntity> entityList = customExcelSheet(exportParams, paramInfo, pojoClass);
        List<Map<String, Object>> dataList = transformData(dataSet);
        Workbook workbook = exportExcel(exportParams, entityList, dataList);
        try {
            downLoadExcel(fileName, response, workbook);
        } catch (Exception e) {
            throw new BusinessException(McnErrorCode.EXPORT_EXCEPTION);
        }
    }

    private static Workbook exportExcel(ExportParams entity, List<ExcelExportEntity> entityList,
                                       Collection<?> dataSet) {
        Workbook workbook = getWorkbook(entity.getType(), dataSet.size());
        new CustomExcelExportService().createSheetForMap(workbook, entity, entityList, dataSet);
        return workbook;
    }

    private static Workbook getWorkbook(ExcelType type, int size) {
        return new SXSSFWorkbook();
    }

    /**
     * list对象数据转成list<map>
     *
     * @param dataSet
     * @return List<Map<String, Object>>
     * @throws IllegalAccessException
     */
    private static List<Map<String, Object>> transformData(List<?> dataSet) {
        List<Map<String, Object>> retData = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(dataSet)) {
            for (Object data : dataSet) {
                Map<String, Object> map = new HashMap<>(16);
                Class<?> clazz = data.getClass();
                for (Field field : clazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    if ("customFieldList".equals(fieldName)) {
                        try {
                            Map fieldMap = (Map) field.get(data);
                            if (fieldMap != null) {
                                map.putAll(fieldMap);
                            }
                        } catch (IllegalAccessException e) {
                            throw new BusinessException(McnErrorCode.EXPORT_EXCEPTION);
                        }
                    } else {
                        Object value = null;
                        try {
                            value = field.get(data);
                        } catch (IllegalAccessException e) {
                            throw new BusinessException(McnErrorCode.EXPORT_EXCEPTION);
                        }
                        map.put(fieldName, value);

                    }
                }
                retData.add(map);
            }
        }
        return retData;
    }

    /**
     * 生成表头，基础字段列名不能相同
     */
    private static List<ExcelExportEntity> customExcelSheet(ExportParams entity, List<ExcelCustom> paramInfo, Class<?> pojoClass) {
        //表头的集合，用于添加表头
        List<ExcelExportEntity> entityList = new ArrayList<>();

        Map<String, ExcelExportEntity> excelCustomMap = new HashMap<>(16);
        if (entity != null && pojoClass != null) {
            try {
                List<ExcelExportEntity> excelParams = new ArrayList<>();
                Field[] fields = PoiPublicUtil.getClassFields(pojoClass);
                Map<String, String> fieldMap = new HashMap<>(16);
                for (Field field : fields) {
                    Excel excel = field.getAnnotation(Excel.class);
                    if (excel != null) {
                        fieldMap.put(excel.name(), field.getName());
                    }
                }
                ExcelTarget etarget = (ExcelTarget) pojoClass.getAnnotation(ExcelTarget.class);
                String targetId = etarget == null ? null : etarget.value();
                (new ExcelExportService()).getAllExcelField(entity.getExclusions(), targetId, fields, excelParams, pojoClass, (List) null, (ExcelEntity) null);
                for (ExcelExportEntity excelExportEntity : excelParams) {
                    if (excelExportEntity.getKey() != null) {
                        excelCustomMap.put(excelExportEntity.getKey().toString(), excelExportEntity);
                    } else if (excelExportEntity.getName() != null) {
                        excelExportEntity.setKey(fieldMap.get(excelExportEntity.getName()));
                        excelCustomMap.put(fieldMap.get(excelExportEntity.getName()), excelExportEntity);
                    }
                }
            } catch (Exception var9) {
                throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, var9.getCause());
            }
        } else {
            throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
        }
        for (ExcelCustom excelCustomDTO : paramInfo) {
            if (excelCustomMap.containsKey(excelCustomDTO.getKey())) {
                entityList.add(excelCustomMap.get(excelCustomDTO.getKey()));
            }
        }

        return entityList;
    }

    public static <T> List<T> importExcel(MultipartFile file, Class<T> pojoClass) {
        return importExcel(file, 0, 1, pojoClass);
    }

    public static <T> List<T> importBigExcel(MultipartFile file, Class<T> pojoClass) {
        return importBigExcel(file, 0, 1, pojoClass, (targetList) -> new IReadHandler<T>() {
            @Override
            public void handler(T o) {
                targetList.add(o);
            }

            @Override
            public void doAfterAll() {

            }
        });
    }

    public static List<Map<String,Object>> importExcelForMap(MultipartFile file) throws Exception {
        if (file == null) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_CANT_BE_EMPTY);
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(0);
        params.setHeadRows(1);
        try {
            return new CustomExcelImportService().importExcelByIs(file.getInputStream(), Map.class, params, false).getList();
        } catch (NoSuchElementException e) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_CANT_BE_EMPTY);
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }

    /**
     * 大批量导入
     * @param file
     * @param titleRows
     * @param headerRows
     * @param pojoClass
     * @param <T>
     * @return
     */
    public static <T> List<T> importBigExcel(
            MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass, Function<List<T>, IReadHandler<T>> readHandlerFunc) {
        if (file == null) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_CANT_BE_EMPTY);
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        List<T> targetList = new ArrayList<>();
        try {
            ExcelImportUtil.importExcelBySax(file.getInputStream(), pojoClass, params, readHandlerFunc.apply(targetList));
            return targetList;
        } catch (NoSuchElementException e) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_CANT_BE_EMPTY);
        }  catch (Exception e) {
            log.error("excel导入异常：", e);
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_FAILED);
        }
    }

    public static <T> List<T> importExcel(
            MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
        if (file == null) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_CANT_BE_EMPTY);
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        try {
            return new CustomExcelImportService().importExcelByIs(file.getInputStream(), pojoClass, params, true).getList();
        } catch (NoSuchElementException e) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_CANT_BE_EMPTY);
        } catch (Exception e) {
            log.error("excel导入异常：", e);
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_FAILED);
        }
    }

    public static <T extends AbstractExcelVerifyModel> List<T> importVerifyExcel(MultipartFile file, Class<T> pojoClass) {
        if (file == null) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_CANT_BE_EMPTY);
        }
        ImportParams params = new ImportParams();
        params.setNeedVerify(true);
        params.setTitleRows(0);
        params.setHeadRows(1);
        try {
            ExcelImportResult excelImportResult = new CustomExcelImportService().importExcelByIs(file.getInputStream(), pojoClass, params, true);
            List<T> totalList = excelImportResult.getList();
            totalList.addAll(excelImportResult.getFailList());
            Collections.sort(totalList, Comparator.comparingInt(o -> o.getRowNum().intValue()));
            return totalList;
        } catch (NoSuchElementException e) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_CANT_BE_EMPTY);
        } catch (Exception e) {
            log.error("excel导入异常：", e);
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_FAILED);
        }
    }
}
